import cv2
import rasterizer
import numpy as np
from math import sin, cos, tan


def get_model_matrix(rotation_angle):
    """
    场景物体绕z轴旋转,绕任意向量旋转有其他公式

    Parameters
    ----------
    rotation_angle: float

    Returns
    -------
    model: ndarray of shape (4, 4)
        场景物体的旋转变换矩阵 ndarray of shape (4, 4)
    """
    model = np.eye(4)
    angle = rotation_angle / 180 * np.pi
    translate = np.array([
        cos(angle), -sin(angle), 0, 0
        , sin(angle), cos(angle), 0, 0
        , 0, 0, 1, 0
        , 0, 0, 0, 1
    ])

    # translate = np.array([
    #     1, 0, 0, 0,
    #     0, cos(angle), -sin(angle), 0,
    #     0, sin(angle), cos(angle), 0,
    #     0, 0, 0, 1
    # ])

    model = np.dot(translate.reshape(4, 4), model)

    return model


def get_view_matrix(eye_pos):
    """
    相机和所有物体平移到原点，此处省略了相机的旋转

    Parameters
    ----------
    eye_pos: list
        相机位置向量

    Returns
    -------
    view: ndarray of shape (4, 4)
        相机变换向量矩阵 ndarray of shape (4, 4)
    """
    view = np.eye(4)
    translate = np.eye(4)
    for i in range(3):
        translate[i][3] = -eye_pos[i]

    view = np.dot(translate, view)

    return view


def get_projection_matrix(eye_fov, aspect_ratio, zNear, zFar):
    """
    返回一个透视投影的变换向量矩阵

    Parameters
    ----------
    eye_fov: float
        可视角度
    aspect_ratio: float
        场景的宽高比
    zNear: float
        场景的近平面（自定义）
    zFar: float
        场景的远平面（自定义）
    """
    eye_fov_angle = eye_fov / 360 * np.pi
    t = tan(eye_fov_angle) * zNear
    b = -t
    r = aspect_ratio * t
    l = -r
    zNear = -zNear
    zFar = -zFar

    """分步骤计算
    pers_ortho = np.array([
        zNear, 0, 0, 0,
        0, zNear, 0, 0,
        0, 0, zNear + zFar, -(zNear * zFar),
        0, 0, 1, 0
    ]).reshape(4, 4)

    ortho1 = np.array([
        2 / (r - l), 0, 0, 0,
        0, 2 / (t - b), 0, 0,
        0, 0, 2 / (zNear - zFar), 0,
        0, 0, 0, 1
    ]).reshape(4, 4)

    ortho2 = np.array([
        1, 0, 0, -(r + l) * 0.5,
        0, 1, 0, -(t + b) * 0.5,
        0, 0, 1, -(zNear + zFar) * 0.5,
        0, 0, 0, 1
    ]).reshape(4, 4)

    projection = np.eye(4)
    projection = np.dot(pers_ortho, projection)
    projection = np.dot(ortho2, projection)
    projection = np.dot(ortho1, projection)"""

    # 化简版本
    projection = np.array([2 * zNear / (r - l), 0, -(r + l) / (r - l), 0,
                           0, 2 * zNear / (t - b), -(t + b) / (t - b), 0,
                           0, 0, (zNear + zFar) / (zNear - zFar), -(2 * zNear * zFar) / (zNear - zFar),
                           0, 0, 1, 0]).reshape(4, 4)

    return projection


if __name__ == '__main__':

    r = rasterizer.Rasterizer(700, 700)

    angle = 0
    eye_pos = [0, 0, 5]
    triangle_pos = [[2, 0, -2], [0, 2, -2], [-2, 0, -2]]
    triangle_index = [0, 1, 2]

    ind_id = r.load_indices(triangle_index)
    pos_id = r.load_positions(triangle_pos)

    key = 0
    frame_count = 0
    while key != 27:
        r.clear()
        model = get_model_matrix(angle)
        view = get_view_matrix(eye_pos)
        projection = get_projection_matrix(45, 1, 0.1, 50)
        r.set_mvp(model, view, projection)

        r.draw(pos_id, ind_id)
        image = r.convert_to_8uc3()

        cv2.imshow('image', image)
        print('frame count:{0}, angle = {1}'.format(frame_count, angle))
        angle = angle + 10 if angle + 10 <= 360 else angle % 360

        key = cv2.waitKey(100)
